#ifndef MESSAGE_HEADER_H_INCLUDED
#define MESSAGE_HEADER_H_INCLUDED

/*
* Copyright (c) 2010 Michael Collins
*
* This file is part of TerraFirma.
*
* TerraFirma is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TerraFirma is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TerraFirma.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <string.h>
#include <context.h>
#ifdef DEBUG
#include <iostream>
#endif

namespace engine
{
    typedef uint32_t task_id; //use this hash number as an identifier for tasks
    typedef uint8_t msg_type;
    typedef uint8_t hostid; //Identifier of the host
    typedef uint8_t packet_count; //for use only by the network manager
    //Assumes that packets get out of order by no more than 255 packets sequentially

    /** message_header
     *  this is a new header, and is changed from the previous type due to type-safety errors.
     *  This header model is much better, and is designed to be derived by the different message types
     *  however, it commits some inheritance taboos, namely copy constructors and non-virtual members
     *  (we can't communicate a vtable over the network or across save-games)
     *  This is because the size of the message is encoded into the header as the size member, so as to
     *  reduce the number of unnecessary copies and reduce network load substancially
     *
     *  Derivations of this cannot implement any virtual methods, but they each need their own copy and assign
     *  constructors, and destructors are not allowed to throw exceptions. Furthermore, you can't call delete
     *  on a down-casted pointer to a message header
     */

    //Upper 4 bits of the 'type' field indicate the major destination for the message
    //i.e. how to interpret the message
#define NULL_MSG    0x0 << 4
#define CONTROL_MSG 0x1 << 4
#define SOUND_MSG   0x2 << 4
#define GAME_MSG    0x3 << 4
#define NETWORK_MSG 0x4 << 4
#define VIDEO_MSG   0x5 << 4
#define BCAST_PAUSE 0xD << 4
#define BCAST_CLOSE 0xE << 4
#define BCAST_ERROR 0xF << 4

#define MIN(a,b) ((a)>(b)?(b):(a))

//A macro to find the largest structure in a variadic list
//supply this with a list of structs and returns the size of the largest one
//#define MAX_SIZE (...)
#define MAX (a, b) (((a) > (b))? (a) : (b))
#define MAX4(a, b, c, d) (MAX(MAX(a,b), MAX(c,d)))
#define MAX8(a,b,c,d,e,f,g,h) (MAX(MAX4(a,b,c,d), MAX4(e,f,g,h)))
#define MAX16(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) (MAX(MAX8(a,b,c,d,e,f,g,h), MAX8(i,j,k,l,m,n,o,p)))

    struct message_header
    {
public:
        //ctors
        message_header() : type(NULL_MSG | 0x00), src_host(context::Instance().hostid),
                size(sizeof(message_header)) {}
        message_header(task_id t, uint8_t m, uint8_t n = 0, uint16_t s = sizeof(message_header)) :
                dst_task(t), type(m | n), src_host(context::Instance().hostid), size(s) {}
        //Accessor functions.
        msg_type get_type() const
        {
            return type;
        }
        msg_type major_type() const
        {
            return type & 0xf0;
        }
        msg_type minor_type() const
        {
            return type & 0xf;
        }
        hostid get_src_host() const
        {
            return src_host;
        }
        uint16_t get_size() const
        {
            return size;
        }

        task_id dst_task;
private:
        const msg_type type; //Enumerates how programs should interpret this message
        const hostid src_host; //The host where the message originated
        const uint16_t size; //The size of the message, including the header (but not the network header)
        friend class mpi;
    };

    #ifdef DEBUG
    //Prints out the type of the header
    std::ostream& operator << (std::ostream& os, const message_header& m);
    #endif //DEBUG

    /**Template for defining a message that goes into a message queue
    */
    template <size_t S>
    struct message
    {
    public:
        //Copy constructor is special in that it only copies as little data as necessary
        message( const message<S>& m ) {memcpy(this, &m, MIN(S, m.header().get_size()));}
        message( const message_header& m ) {memcpy(this, &m, MIN(S, m.get_size()));}
        message( const message_header* m ) {memcpy(this, m, MIN(S, m->get_size()));}
        message& operator= (const message<S>& m) {memcpy(this, &m, MIN(S, m.header().get_size())); return *this;}

        //Header access
        //message_header& header() const {return *(reinterpret_cast<message_header*>(this));}
        const message_header& header() const {return *(reinterpret_cast<const message_header*>(this));}
        uint8_t type() const {return (header().type & 0xf);}

        //Upcasts a message to a derived type
        //TODO: do a safety check
        template <typename T>
        const T& cast() const {return *(reinterpret_cast<const T*>(this));}

        //message_header header;
        uint8_t payload [S];
    };
}
#endif // MESSAGE_HEADER_H_INCLUDED
